#ifndef PERFORM_AND_MEASURE_TASK
#define PERFORM_AND_MEASURE_TASK

#include <ctime>
#include <cstdlib>

#if defined(__linux__) 
	#include <sys/time.h>
#endif

using namespace std;

/**
 *  @brief check clock_t object
 *  @param t a clock_t object
 *  
 *  Check t is valid. Otherwise, terminate the program.
 */
inline void check_clock(const clock_t& t)
{
	if(t == clock_t(-1))
	{
		cerr << "sorry, clock error\n";
		exit(1);
	}
}

/**
 *  @enum timing_policy
 *        Timing policy class, used as the first template parameter for 
 *        perform_and_measure_task.
 *  @var  CPU_CLOCK 
 *        timing by clock() based on CPU clocks [default type]
 *  @var  REAL_TIME 
 *        timing by clock_gettime() to get real runing time with 
 *        nanosecond resolution. [!!WARNING!!] This is machine and OS dependent,
 *        which is not guarantee for success and correct timing.
 */
enum class timing_policy { CPU_CLOCK, REAL_TIME };

/**
 *  @brief default implementation class for "perform_and_measure_task" [disabled]
 */
template<timing_policy, class task_type, class ReturnType>
struct perform_and_measure_task_impl { };

#if defined(__linux__) 

/**
 *  @brief  partial specialization implementation class for "perform_and_measure_task"
 *  @tparam task_type any callable type to perform a task by calling () operator
 *  @tparam ReturnType return type
 *  
 *  Only go() function included to run a task and measure its time consuming.
 *  This is partial specialization class for REAL_TIME timing policy
 */
template<class task_type, class ReturnType>
struct perform_and_measure_task_impl<timing_policy::REAL_TIME, task_type, ReturnType>
{
	/**
	 *  @brief      implementation function
	 *  @param[in]  f callable instance type of task_type
	 *  @param[out] returnTime return value of time consuming in seconds
	 *  @return     the value returned by calling f()
	 */
	static auto go(task_type& f, double& returnTime, double& taskTotalTime) -> decltype(f()) 
	{
		struct timespec start, end;

		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
		auto v = f();
		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

		long seconds  = end.tv_sec  - start.tv_sec;
		long nseconds = end.tv_nsec - start.tv_nsec;
		returnTime = seconds + (double)nseconds / 1000000000.0;
		taskTotalTime += returnTime;

		return v;
	}
};

/**
 *  @brief  partial specialization implementation class for "perform_and_measure_task"
 *  @tparam task_type any callable type to perform a task by calling () operator
 *  
 *  Only go() function included to run a task and measure its time consuming.
 *  This is partial specialization class for REAL_TIME timing policy and 
 *  return void tasks
 */
template<class task_type>
struct perform_and_measure_task_impl<timing_policy::REAL_TIME, task_type, void>
{
	/**
	 *  @brief      implementation function
	 *  @param[in]  f callable instance type of task_type with no return values (void)
	 *  @param[out] returnTime return value of time consuming in seconds
	 */
	static void go(task_type& f, double& returnTime, double& taskTotalTime) 
	{
		struct timespec start, end;

		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
		f();
		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

		long seconds  = end.tv_sec  - start.tv_sec;
		long nseconds = end.tv_nsec - start.tv_nsec;
		returnTime = seconds + (double)nseconds / 1000000000.0;
		taskTotalTime += returnTime;
	}
};

#endif //! __linux__

/**
 *  @brief  partial specialization implementation class for "perform_and_measure_task"
 *  @tparam task_type any callable type to perform a task by calling () operator
 *  @tparam ReturnType return type
 *  
 *  Only go() function included to run a task and measure its time consuming.
 *  This is partial specialization class for CPU_CLOCK timing policy
 */
template<class task_type, class ReturnType>
struct perform_and_measure_task_impl<timing_policy::CPU_CLOCK, task_type, ReturnType>
{
	/**
	 *  @brief      implementation function
	 *  @param[in]  f callable instance type of task_type
	 *  @param[out] returnTime return value of time consuming in seconds
	 *  @return     the value returned by calling f()
	 */
	static auto go(task_type& f, double& returnTime, double& taskTotalTime) -> decltype(f()) 
	{
		clock_t t1 = clock();
		check_clock(t1);

		auto v = f();

		clock_t t2 = clock();
		check_clock(t2);

		returnTime = double(t2-t1) / CLOCKS_PER_SEC;
		taskTotalTime += returnTime;
		return v;
	}
};

/**
 *  @brief  partial specialization implementation class for "perform_and_measure_task"
 *  @tparam task_type any callable type to perform a task by calling () operator
 *  
 *  Only go() function included to run a task and measure its time consuming.
 *  This is partial specialization class for CPU_CLOCK timing policy and 
 *  return void tasks
 */
template<class task_type>
struct perform_and_measure_task_impl<timing_policy::CPU_CLOCK, task_type, void>
{
	/**
	 *  @brief      implementation function
	 *  @param[in]  f callable instance type of task_type with no return values (void)
	 *  @param[out] returnTime return value of time consuming in seconds
	 */
	static void go(task_type& f, double& returnTime, double& taskTotalTime) 
	{
		clock_t t1 = clock();
		check_clock(t1);

		f();

		clock_t t2 = clock();
		check_clock(t2);

		returnTime = double(t2-t1) / CLOCKS_PER_SEC;
		taskTotalTime += returnTime;
	}
};

/**
 *  @brief      dispatch function for "perfom a task and measure its time consuming"
 *  @tparam     timing_type timing policy class.
 *              It should be timing_policy::CPU_CLOCK [default] or timing_policy::REAL_TIME
 *  @tparam     task_type any callable type to perform a task by calling () operator
 *  @param[in]  f callable instance type of task_type
 *  @param[out] returnTime return value of time consuming in seconds
 *  @return     the value returned by calling f() (void or non-void)
 */

template<timing_policy timing_type = timing_policy::CPU_CLOCK>
class task_timer
{
public:
	task_timer() { taskTime = 0.; taskTotalTime = 0.; }

	template<class task_type>
	auto perform_and_measure(task_type& f) -> decltype(f())
	{  return perform_and_measure_task_impl<timing_type, task_type, decltype(f())>::go(f, taskTime, taskTotalTime);  }

	template<class task_type>
	auto operator()(task_type& f) -> decltype(f())
	{  return perform_and_measure(f); }

	double get_task_time() const { return taskTime; }
	double get_total_time() const { return taskTotalTime; }
	void set_zero() { taskTotalTime = 0.; }

private:
	double taskTime;
	double taskTotalTime;
};
#endif //! PERFORM_AND_MEASURE_TASK
